package oa.hleast.xswl.service.pay.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.RefundAmount;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import oa.hleast.common.pay.config.WxPayConfig;
import oa.hleast.common.pay.model.v3.SPRefundModel;
import oa.hleast.xswl.service.pay.WeiXinPayClientService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.cert.X509Certificate;
import java.util.Map;

@Slf4j
@Service
@Transactional
@AllArgsConstructor
public class WeixinPayClientServiceImpl implements WeiXinPayClientService {

    private final static int OK = 200;
    private final WxPayConfig wxPayConfig;

    /**
     * 微信统一下单
     * https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_1
     *
     * @param openId      支付方openId
     * @param sysTradeNo  系统订单号
     * @param subAppId    子商家
     * @param subMchId    微信支付分配的子商户号
     * @param orderTotal  订单总金额,单位分
     * @param description 描述
     * @param attach      自定义参数
     * @return 示例如下:
     * {"id":"eef91ff8-7f2d-5b0e-b6b6-5f507d2b7eb6","create_time":"2021-10-21T17:00:19+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"ixoa7LZjojqvtmXdG33NdiK89se7dJXnOt7K2au2eDUnHelaEQ1oPgb8GvqDg2tAs+YbFxfqMbRAJSna4xOyrMRfLkzvHL6daAyo+RI00pcki5SMrFs6tVP1FqFmFvJJHxrUa/qqWJkdAonB3/3pA/YiOt/1i7fN3qKK+LCN2Nd6EXW3Cv/ZiybIVlBt6Y5YRR3DDYmDyi5si0D6Opdyh0nHGvpooolZzLrEjSj0OkbRMqR+jjDjz0FIY8fv3Do/mVZ6JzoE5E+3l5t7CvDtDfe/e6NAOM9SgSG9VFDJbpdLOIdLg2joMJ+S9YswwYGx+5hh6kL2nB/828fN3WOKOscB6NMPfdP+3Tts5K8Oxhq7HfpJmkpEw75lGgul+b+FZXTBjkxhHh+BI4Vk1WT6zbzwZzaP6xFB2ZowUfqguXOpmx0q6ynkduicecIGvVVJTOcef6WmozuCVMDTWjQexMFxqqGWweOYl6oGHAUFXoGtmFCF4Zqk5yGu66688ArtM/hQ4GwW2pSM4nZRKndGgf7L+f3zZY0GgF/pgbDHnJMvdMzCdxA+lxmD/XWh0vEySjWXJb3Pl2pY5gsyhZu7sg//8Ng7rBqe7Yv94ADdcyffLSNBCDVpKIFweSYHmM9iv0gJ51/N7kOA5Lm0SOnd75weXXXxcve74ymXMGxJmDRuAyjYzLcxYn2qUocIZ+54FX7ix9SvhQBTxws8IRFIH2y2YNn9Q82Sh+2J6ymdDMmevubL0/G1pbaXUJejPBl4g723","associated_data":"transaction","nonce":"Ucqb6wWHDaRE"}},currentTime = 1634806819979
     */
    @Override
    public String jsApiPay(String openId, String sysTradeNo, String subAppId, String subMchId, Integer orderTotal, String description, String attach) {
        {
            try {
                String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
                UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                        .setSp_appid(wxPayConfig.getSpAppid())
                        .setSp_mchid(wxPayConfig.getSpMchid())
                        .setSub_appid(subAppId)
                        .setSub_mchid(subMchId)
                        .setDescription(description)
                        .setOut_trade_no(sysTradeNo)
                        .setTime_expire(timeExpire)
                        .setAttach(attach)
//                        .setNotify_url(wxPayConfig.getDomain().concat("/v3/payNotify"))
                        .setNotify_url(wxPayConfig.getDomain())
                        .setAmount(new Amount().setTotal(orderTotal))
                        .setPayer(new Payer().setSub_openid(openId));

                log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));

                IJPayHttpResponse response = WxPayApi.v3(
                        com.ijpay.core.enums.RequestMethod.POST,
                        WxDomain.CHINA.toString(),
                        WxApiType.PARTNER_JS_API_PAY.toString(),
                        wxPayConfig.getSpMchid(),
                        getSerialNumber(),
                        null,
                        wxPayConfig.getSpKey(),
                        JSONUtil.toJsonStr(unifiedOrderModel)
                );
                log.info("统一下单响应 {}", response);

                if (response.getStatus() == OK) {
                    // 根据证书序列号查询对应的证书来验证签名结果
                    boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getSpWxCert());
                    log.info("verifySignature: {}", verifySignature);
                    if (verifySignature) {
                        String body = response.getBody();
                        JSONObject jsonObject = JSONUtil.parseObj(body);
                        String prepayId = jsonObject.getStr("prepay_id");
                        Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayConfig.getSubAppid(), prepayId, wxPayConfig.getSpKey());
                        log.info("唤起支付参数:{}", map);
                        return JSONUtil.toJsonStr(map);
                    }
                }
                return JSONUtil.toJsonStr(response);
            } catch (Exception e) {
                e.printStackTrace();
                return e.getMessage();
            }
        }
    }

    @Override
    public String jsApiPay(String openId) {
        {
            try {
                String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
                UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                        .setSp_appid(wxPayConfig.getSpAppid())
                        .setSp_mchid(wxPayConfig.getSpMchid())
                        .setSub_appid(wxPayConfig.getSubAppid())
                        .setSub_mchid(wxPayConfig.getSubMchid())
                        .setDescription("服务商V3支付测试")
                        .setOut_trade_no(PayKit.generateStr())
                        .setTime_expire(timeExpire)
                        .setAttach("我是自定义参数")
//                        .setNotify_url(wxPayConfig.getDomain().concat("/v3/payNotify"))
                        .setNotify_url(wxPayConfig.getDomain())
                        .setAmount(new Amount().setTotal(1))
                        .setPayer(new Payer().setSub_openid(openId));

                log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));

                IJPayHttpResponse response = WxPayApi.v3(
                        com.ijpay.core.enums.RequestMethod.POST,
                        WxDomain.CHINA.toString(),
                        WxApiType.PARTNER_JS_API_PAY.toString(),
                        wxPayConfig.getSpMchid(),
                        getSerialNumber(),
                        null,
                        wxPayConfig.getSpKey(),
                        JSONUtil.toJsonStr(unifiedOrderModel)
                );
                log.info("统一下单响应 {}", response);

                if (response.getStatus() == OK) {
                    // 根据证书序列号查询对应的证书来验证签名结果
                    boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getSpWxCert());
                    log.info("verifySignature: {}", verifySignature);
                    if (verifySignature) {
                        String body = response.getBody();
                        JSONObject jsonObject = JSONUtil.parseObj(body);
                        String prepayId = jsonObject.getStr("prepay_id");
                        Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayConfig.getSubAppid(), prepayId, wxPayConfig.getSpKey());
                        log.info("唤起支付参数:{}", map);
                        return JSONUtil.toJsonStr(map);
                    }
                }
                return JSONUtil.toJsonStr(response);
            } catch (Exception e) {
                e.printStackTrace();
                return e.getMessage();
            }
        }
    }


    /**
     * 微信退款
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
     *
     * @param transactionId 原支付交易对应的微信订单号 outTradeNo 原支付交易对应的商户订单号 二选一
     * @param outRefundNo   商户退款单号 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。 PayKit.generateStr()
     * @param reason        退款原因 若商户传入，会在下发给用户的退款消息中体现退款原因
     * @param refund        退款金额
     * @param total         原订单金额
     */
    @Override
    public Boolean refund(String transactionId, String outTradeNo, String outRefundNo, Integer refund, Integer total, String reason) {
        try {
            log.info("商户退款单号: {}", outRefundNo);

            SPRefundModel refundModel = new SPRefundModel();
            if (StrUtil.isNotEmpty(transactionId)) {
                refundModel.setTransaction_id(transactionId);
            }
            if (StrUtil.isNotEmpty(outTradeNo)) {
                refundModel.setOut_trade_no(outTradeNo);
            }

            refundModel.setSub_mchid(wxPayConfig.getSubMchid())
                    .setOut_refund_no(outRefundNo)
                    .setReason(reason)
                    .setNotify_url(wxPayConfig.getDomain())
                    .setAmount(new RefundAmount().setRefund(refund).setTotal(total).setCurrency("CNY"));


            log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.DOMESTIC_REFUNDS.toString(),
                    wxPayConfig.getSpMchid(),
                    getSerialNumber(),
                    null,
                    wxPayConfig.getSpKey(),
                    JSONUtil.toJsonStr(refundModel)
            );
            // 根据证书序列号查询对应的证书来验证签名结果
            log.info("退款响应 {}", response);
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getSpWxCert());
            log.info("verifySignature: {}", verifySignature);

            if (verifySignature) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("{}", e.getMessage(), e);
            return false;
        }
        return false;
    }


    private static String serialNo;

    private String getSerialNumber() {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayConfig.getSpCert()));
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }
}
